import 'dart:async';

import 'package:casdon/app/config/env/common_config.dart';
import 'package:casdon/app/config/env/env_provider.dart';
import 'package:casdon/app_providers.dart';
import 'package:casdon/common/model/tuya_category.dart';
import 'package:casdon/common/service/base_service.dart';
import 'package:casdon/core/auth/auth_storage.dart';
import 'package:casdon/core/extentions/string_validation.dart';
import 'package:casdon/core/logger/log.dart';
import 'package:casdon/core/utils/shared_preferences_utils.dart';
import 'package:casdon/auto_router.dart';
import 'package:casdon/modules/me/application/notification/app_version_info_service.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:tuya_home_sdk_flutter/tuya_home_sdk_flutter.dart';

enum PanelFromType { home, qr_recipe, recipe_cook, nfc }

class TuyaService implements BaseService {
  ThingSmartHomeModel? currentHome;
  List<ThingSmartDeviceModel>? devList;
  StreamSubscription? _homeInfoSub;
  StreamSubscription? _deviceInfoSub;
  static final TuyaService _instance = TuyaService._internal();
  factory TuyaService() => _instance;
  TuyaService._internal();
  static const _sessionLossCodes = {'1509', '1510', '1501'};
  static const _maxSessionLossRetry = 3;
  int _sessionLossCounter = 0;
  int _tuyaLoginAttemptCount = 0;
  static const _sessionLossKeyword = 'USER_SESSION';

  StreamSubscription<ThingSmartDeviceModel>? _discoverySubscription;
  String? _currentBatchToken;
  bool _isBatchConfigRunning = false;
  String? _batchSsid;
  String? _batchPassword;
  final Set<String> _batchAppendedUuids = <String>{};
  Future<bool>? _loginInProgress;

  @override
  Future<void> init() async {
    await _runWithTuyaHandling(
      () => TuyaHomeSdkFlutter.instance.initSdk(
        CommonConfig.tuya_appKey,
        CommonConfig.tuya_appSecret,
        '',
        isDebug: kDebugMode,
      ),
      onError: (_, __) async {},
    );
  }

  // 涂鸦登录
  Future<bool> login(String userName, String pwd) async {
    return _performLogin(userName, pwd);
  }

  // 涂鸦登录
  Future<bool> getLatestMessage() async {
    return _runWithTuyaHandling<bool>(
      () => TuyaHomeSdkFlutter.instance.getlatestMessage(),
      onError: (_, __) async => false,
    );
  }

  Future<void> gotoPanel({
    required String deviceId,
    required String pid,
    required PanelFromType fromType,
  }) async {
    final storage = getProvider(authStorageProvider.notifier);
    final token = await storage.getToken();
    final uid = await storage.getUserId();
    final userName = await storage.getUserAccount();

    final appVersion = await getProvider(appVersionProvider.future);
    final version = appVersion.version;
    final envNotifier = getProvider(environmentProvider.notifier);
    final envName = envNotifier.getEnvName();
    var context = {
      "fromType": fromType.name,
      "token": token,
      "clientId": CommonConfig.app_clientID,
      "userId": uid,
      "userName": userName,
      "tuyaDid": deviceId,
      "tuyaPid": pid,
      "appVersion": version,
      "env": envName,
    };
    final initialPropos = {"context": context, "router": {}, "config": {}};
    await _runWithTuyaHandling(
      () => TuyaHomeSdkFlutter.instance.gotoPanel(
        deviceId: deviceId,
        initialPropos: initialPropos,
        contextProps: null,
      ),
      onError: (_, __) async {},
    );
  }

  Future<void> gotoMessage() async {
    await _runWithTuyaHandling(
      () => TuyaHomeSdkFlutter.instance.gotoMessage(),
      onError: (_, __) async {},
    );
  }

  void onDeviceInfoUpdated(
    String devId,
    void Function(dynamic event)? handler,
  ) {
    _deviceInfoSub?.cancel();

    _deviceInfoSub = TuyaHomeSdkFlutter.instance
        .onDeviceDpsUpdated(deviceId: devId)
        .listen(handler, onError: null, onDone: null);
    ;
  }

  Future<bool> renameDevice({
    required String deviceId,
    required String name,
  }) async {
    return _runWithTuyaHandling<bool>(
      () => TuyaHomeSdkFlutter.instance.renameDevice(
        deviceId: deviceId,
        name: name,
      ),
      onError: (_, __) async => false,
    );
  }

  Future<bool> get isloggedIn async {
    return _runWithTuyaHandling<bool>(
      () => TuyaHomeSdkFlutter.instance.isLogin(),
      onError: (_, __) async => false,
    );
  }

  // 涂鸦退出
  Future<bool> logOut() async {
    return _runWithTuyaHandling<bool>(
      () => TuyaHomeSdkFlutter.instance.logout(),
      onError: (_, __) async => false,
    );
  }

  // 获取当前家庭
  Future<ThingSmartHomeModel> getCurrentHome() async {
    return _runWithTuyaHandling(() async {
      final list = await TuyaHomeSdkFlutter.instance.getHomeList();
      if (list.isEmpty) {
        throw Exception('未获取到家庭信息');
      }
      currentHome = list.first;
      return currentHome!;
    }, onError: (error, stack) => Future.error(error, stack));
  }

  Future<bool> addShareDevice({
    required String userAccount,
    required List<String> devIdList,
  }) async {
    return _runWithTuyaHandling<bool>(() async {
      currentHome = await getCurrentHome();
      if (currentHome == null) {
        Log.w('当前家庭为空，无法共享设备');
        return false;
      }
      return await TuyaHomeSdkFlutter.instance.addShareMember(
        userAccount: userAccount,
        devIdList: devIdList,
        homeId: currentHome!.homeId,
      );
    }, onError: (_, __) async => false);
  }

  Future<List<ThingSmartShareMemberModel>> getMemberShareList({
    required String devId,
  }) async {
    return _runWithTuyaHandling(
      () => TuyaHomeSdkFlutter.instance.getMemberShareList(devid: devId),
      onError: (_, __) async => [],
    );
  }

  Future<bool> removeShareMember({required int memberId}) async {
    return _runWithTuyaHandling<bool>(
      () => TuyaHomeSdkFlutter.instance.removeShareMember(memberId: memberId),
      onError: (_, __) async => false,
    );
  }

  // 当前home设备列表
  Future<List<ThingSmartDeviceModel>> homeDeviceList() async {
    Log.w('涂鸦开始获取设备：homeDeviceList');
    return _runWithTuyaHandling(() async {
      currentHome = await getCurrentHome();
      if (currentHome == null) {
        Log.w('当前家庭为空，无法获取设备列表');
        return <ThingSmartDeviceModel>[];
      }
      Log.i('当前家庭，homeId: ${currentHome!.homeId}');
      devList = await TuyaHomeSdkFlutter.instance.getHomeDevices(
        homeId: currentHome!.homeId,
      );
      Log.i('当前家庭涂鸦设备: ${devList?.length ?? 0} 台 devices');
      return devList ?? [];
    }, onError: (e, s) async => <ThingSmartDeviceModel>[]);
  }

  // 获取配网列表一级品类数据
  Future<List<TuyaLevel1Category>> requestPrimaryCategory() async {
    return _runWithTuyaHandling(() async {
      final ret = await TuyaHomeSdkFlutter.instance.requestPrimaryCategory();
      if (ret != null) {
        List<TuyaLevel1Category> list = TuyaACategory.fromJson(ret).level1List;
        return list;
      }
      return <TuyaLevel1Category>[];
    }, onError: (_, __) async => []);
  }

  // 获取配网列表二级品类数据
  Future<List<TuyaLevel2Category>> requestSecondaryCategory(
    String levelOneCode,
    String type,
  ) async {
    return _runWithTuyaHandling(() async {
      final ret = await TuyaHomeSdkFlutter.instance.requestSecondaryCategory(
        levelOneCode,
        type,
      );
      if (ret != null) {
        List<TuyaLevel2Category> list = TuyaACategoryLevel2Model.fromJson(
          ret,
        ).level3Items;
        return list;
      }
      return <TuyaLevel2Category>[];
    }, onError: (_, __) async => []);
  }

  // 获取配网列表三级品类详情数据
  Future<ThingSmartDeviceCategoryModel?> requestCategoryDetail(
    String bizValue,
    int bizType,
  ) async {
    return _runWithTuyaHandling(
      () =>
          TuyaHomeSdkFlutter.instance.requestCategoryDetail(bizType, bizValue),
      onError: (_, __) async => null,
    );
  }

  Future<bool> publishDps({
    required String deviceId,
    required Map<String, dynamic> dps,
  }) async {
    return _runWithTuyaHandling<bool>(
      () =>
          TuyaHomeSdkFlutter.instance.publishDps(deviceId: deviceId, dps: dps),
      onError: (_, __) async => false,
    );
  }

  Stream<ThingSmartDeviceModel> discoverDevices() {
    final controller = StreamController<ThingSmartDeviceModel>();

    Future<void> start() async {
      try {
        final stream = TuyaHomeSdkFlutter.instance.discoverDevices();
        await controller.addStream(stream);
        await controller.close();
      } on PlatformException catch (e, st) {
        Log.e('discoverDevices PlatformException', e, st);
        final recovered = await _tryRecoverSession(e);
        if (recovered) {
          await start();
          return;
        }
        if (!controller.isClosed) {
          controller.addError(e, st);
          await controller.close();
        }
      } catch (error, st) {
        Log.e('discoverDevices error', error, st);
        if (!controller.isClosed) {
          controller.addError(error, st);
          await controller.close();
        }
      }
    }

    unawaited(start());
    return controller.stream;
  }

  void onHomeInfoUpdated(void Function(dynamic event)? handler) async {
    // 如果之前有监听，先取消，防止重复订阅
    _homeInfoSub?.cancel();

    if (currentHome == null) {
      Log.w('当前家庭为空，无法获取设备列表');
      currentHome = await getCurrentHome();
    }
    _homeInfoSub = TuyaHomeSdkFlutter.instance
        .onHomeInfoUpdated(homeId: currentHome?.homeId ?? 0)
        .listen(handler, onError: null, onDone: null);
  }

  // 停止搜索
  Future<void> discoverStop({bool cleanCache = false}) async {
    await _runWithTuyaHandling(
      () => TuyaHomeSdkFlutter.instance.stopDiscoverDevices(),
      onError: (_, __) async {},
    );
  }

  Stream<BleBatchConfigResult> batchConfigStream() {
    return TuyaHomeSdkFlutter.instance.bleBatchConfigStream();
  }

  Future<bool> startBatchBleConfig({
    required List<String> uuids,
    required String ssid,
    required String password,
  }) async {
    if (uuids.isEmpty) return false;
    return _runWithTuyaHandling<bool>(() async {
      currentHome ??= await getCurrentHome();
      final home = currentHome!;
      if (_batchSsid != ssid || _batchPassword != password) {
        await stopBatchBleConfig();
        _batchSsid = ssid;
        _batchPassword = password;
      }
      if (_currentBatchToken == null || _currentBatchToken!.isEmpty) {
        final token = await TuyaHomeSdkFlutter.instance.requestHomeToken(
          homeId: home.homeId,
        );
        if (token == null || token.isEmpty) {
          throw Exception('无法获取配网Token');
        }
        _currentBatchToken = token;
      }
      for (final uuid in uuids) {
        if (uuid.isEmpty) continue;
        final normalized = uuid.toLowerCase();
        if (_batchAppendedUuids.add(normalized)) {
          await TuyaHomeSdkFlutter.instance.batchAppendBleDevice(uuid: uuid);
        }
      }
      if (!_isBatchConfigRunning) {
        await TuyaHomeSdkFlutter.instance.startBatchConfigWifi(
          ssid: ssid,
          password: password,
          token: _currentBatchToken!,
        );
        _isBatchConfigRunning = true;
      }
      return true;
    }, onError: (_, __) async => false);
  }

  Future<void> stopBatchBleConfig() async {
    await _runWithTuyaHandling(() async {
      await TuyaHomeSdkFlutter.instance.stopBatchConfigWifi();
    }, onError: (_, __) async {});
    _currentBatchToken = null;
    _isBatchConfigRunning = false;
    _batchAppendedUuids.clear();
    _batchSsid = null;
    _batchPassword = null;
  }

  Future<void> stopBleWifiConfig() async {
    await _runWithTuyaHandling(() async {
      await TuyaHomeSdkFlutter.instance.stopConfigWiFi();
    }, onError: (_, __) async {});
  }

  Future<ThingSmartDeviceModel?> startPairNet({
    required String ssid,
    required String password,
    required String deviceUuid,
    required String deviceProductId,
    int timeout = CommonConfig.pairNet_device_timeout,
  }) async {
    if (ssid.isEmpty || password.isEmpty) {
      Log.e("WiFi名称或密码不能为空", Object(), StackTrace.current);
      return Future.value(null);
    }
    if (currentHome == null) {
      Log.e("当前家庭 currentHome为空", Object(), StackTrace.current);
      return Future.value(null);
    }

    try {
      final ret = await TuyaHomeSdkFlutter.instance.startConfigBLEWifiDevice(
        ssid: ssid,
        password: password,
        homeId: currentHome!.homeId,
        deviceUuid: deviceUuid,
        deviceProductId: deviceProductId,
        timeout: timeout,
      );
      if (ret != null) {
        final spUtils = await SPUtils.getInstance();
        await spUtils.setString(ssid, password);
      }
      return ret;
    } on PlatformException catch (e, st) {
      Log.e('TuyaService PlatformException', e, st);
      rethrow;
    } catch (e, s) {
      Log.e("单设备蓝牙配网异常", e, s);
      return null;
    }
  }

  Future<void> openRNDebug() async {
    await _runWithTuyaHandling(
      () => TuyaHomeSdkFlutter.instance.openRNDebugConfig(),
      onError: (_, __) async {},
    );
  }

  // 通过二维码一键配网
  Future<ThingSmartDeviceModel?> startConfigQRCodeDevice({
    required String qrCode,
    required String ssid,
    required String password,
    int? homeId,
    int timeout = 180,
  }) async {
    return _runWithTuyaHandling(() async {
      final hid = homeId ?? (currentHome ?? await getCurrentHome()).homeId;
      final device = await TuyaHomeSdkFlutter.instance.startConfigQRCodeDevice(
        qrCode: qrCode,
        ssid: ssid,
        password: password,
        homeId: hid,
        timeout: timeout,
      );
      return device;
    }, onError: (_, __) async => null);
  }

  // 通过devid 获取 设备模型
  Future<ThingSmartDeviceModel?> getDeviceById({required String devId}) async {
    return _runWithTuyaHandling(
      () => TuyaHomeSdkFlutter.instance.getDeviceById(deviceId: devId),
      onError: (_, __) async => null,
    );
  }

  @override
  Future<void> dispose() async {
    await _runWithTuyaHandling(
      () => TuyaHomeSdkFlutter.instance.logout(),
      onError: (_, __) async {},
    );
    _discoverySubscription?.cancel();
  }

  Future<T> _runWithTuyaHandling<T>(
    Future<T> Function() action, {
    required FutureOr<T> Function(Object error, StackTrace stackTrace) onError,
  }) async {
    var hasRetried = false;

    Future<T> attempt() async {
      try {
        return await action();
      } on PlatformException catch (e, st) {
        Log.e('TuyaService PlatformException', e, st);
        if (!hasRetried && await _tryRecoverSession(e)) {
          hasRetried = true;
          return await attempt();
        }
        return await onError(e, st);
      } catch (error, st) {
        Log.e('TuyaService error', error, st);
        return await onError(error, st);
      }
    }

    return attempt();
  }

  Future<bool> _tryRecoverSession(PlatformException exception) async {
    if (!_isSessionLossException(exception)) {
      _sessionLossCounter = 0;
      return false;
    }
    _sessionLossCounter++;
    if (_sessionLossCounter >= _maxSessionLossRetry) {
      await _handleSessionLossLimit();
      return false;
    }
    final reloginSuccess = await _reLoginTuya();
    if (reloginSuccess) {
      _sessionLossCounter = 0;
      return true;
    }
    if (_maxSessionLossRetry <= 1 ||
        _sessionLossCounter >= _maxSessionLossRetry - 1) {
      await _handleSessionLossLimit();
    }
    return false;
  }

  Future<void> _handleSessionLossLimit() async {
    Log.w('多次涂鸦会话失效，强制清除本地登录信息');
    try {
      await _clearAuthStorage();
    } finally {
      _sessionLossCounter = 0;
    }
  }

  bool _isSessionLossException(PlatformException exception) {
    if (_sessionLossCodes.contains(exception.code)) {
      return true;
    }
    final combined =
        '${exception.message ?? ''}${exception.details ?? ''}${exception.code}';
    return combined.contains(_sessionLossKeyword);
  }

  Future<bool> _reLoginTuya() async {
    final attempt = _sessionLossCounter;
    String? account;
    var loginResult = false;
    var attempted = false;
    try {
      final storage = getProvider(authStorageProvider.notifier);
      account = await storage.getUserAccount();
      final pwd = await storage.getPwd();
      if (account == null || account.isEmpty || pwd == null || pwd.isEmpty) {
        Log.i('涂鸦自动登录跳过：缺少账号或密码');
        return false;
      }

      attempted = true;
      Log.i('涂鸦自动登录（$account）第$attempt次');
      loginResult = await _performLogin(account, pwd, isRetry: true);
      return loginResult;
    } on PlatformException catch (e, st) {
      Log.e('涂鸦重登失败', e, st);
      return loginResult;
    } catch (e, st) {
      Log.e('涂鸦重登异常', e, st);
      return loginResult;
    } finally {
      if (attempted) {
        Log.i('涂鸦自动登录（$account）第$attempt次结果: ${loginResult ? '成功' : '失败'}');
      }
    }
  }

  Future<void> _clearAuthStorage() async {
    try {
      Log.w('无法重新登录涂鸦，清除登录信息');
      final storage = getProvider(authStorageProvider.notifier);
      await storage.clear();
    } catch (e, st) {
      Log.e('清除本地登录信息失败', e, st);
    } finally {
      _navigateToLogin();
    }
  }

  void _navigateToLogin() async {
    try {
      final storage = getProvider(authStorageProvider.notifier);
      await storage.clear();
      appRouter.replaceAll([const AccountInputRoute()]);
    } catch (e, st) {
      Log.e('跳转登录页面失败', e, st);
    }
  }

  Future<bool> _performLogin(
    String userName,
    String pwd, {
    bool isRetry = false,
  }) {
    if (_loginInProgress != null) {
      Log.i('已有涂鸦登录请求进行中，等待结果');
      return _loginInProgress!;
    }
    _tuyaLoginAttemptCount++;
    final attempt = _tuyaLoginAttemptCount;
    final future = _runWithTuyaHandling<bool>(
      () => _loginInternal(userName, pwd, attempt: attempt, isRetry: isRetry),
      onError: (_, __) async => false,
    );
    _loginInProgress = future;
    future.whenComplete(() {
      if (identical(_loginInProgress, future)) {
        _loginInProgress = null;
        _tuyaLoginAttemptCount = 0;
      }
    });
    return future;
  }

  Future<bool> _loginInternal(
    String userName,
    String pwd, {
    required int attempt,
    bool isRetry = false,
  }) async {
    try {
      final alreadyLoggedIn = await TuyaHomeSdkFlutter.instance.isLogin();
      if (alreadyLoggedIn) {
        Log.i('当前已是涂鸦登录状态，跳过新的登录请求');
        return true;
      }
    } on PlatformException catch (e, st) {
      Log.e('检查涂鸦登录状态失败', e, st);
    } catch (e, st) {
      Log.e('检查涂鸦登录状态异常', e, st);
    }
    Log.i('涂鸦登录尝试：第$attempt次，账号：$userName');
    bool result;
    if (userName.isEmail()) {
      result = await TuyaHomeSdkFlutter.instance.loginWithEmailPassword(
        email: userName,
        countryCode: "86",
        password: pwd,
      );
    } else {
      result = await TuyaHomeSdkFlutter.instance.loginWithPhonePassword(
        phone: userName,
        countryCode: "86",
        password: pwd,
      );
    }
    Log.i('涂鸦登录（$userName）尝试结果：第$attempt次${result ? '成功' : '失败'}');
    return result;
  }
}

class WifiConfigInfo {
  final String ssid;
  final String pwd;

  WifiConfigInfo({required this.ssid, required this.pwd});
  Map<String, dynamic> toMap() => {'ssid': ssid, 'pwd': pwd};

  static WifiConfigInfo fromMap(Map<String, dynamic> map) =>
      WifiConfigInfo(ssid: map['ssid'] as String, pwd: map['pwd'] as String);
}
